home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / DJLSR106.ARJ / FILEBUF.CC < prev    next >
C/C++ Source or Header  |  1992-03-30  |  15KB  |  555 lines

  1. //    This is part of the iostream library, providing input/output for C++.
  2. //    Copyright (C) 1991, 1992 Per Bothner.
  3. //
  4. //    This library is free software; you can redistribute it and/or
  5. //    modify it under the terms of the GNU Library General Public
  6. //    License as published by the Free Software Foundation; either
  7. //    version 2 of the License, or (at your option) any later version.
  8. //
  9. //    This library is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. //    Library General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU Library General Public
  15. //    License along with this library; if not, write to the Free
  16. //    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. #pragma implementation "filebuf.h"
  18.  
  19. #include "ioprivate.h"
  20. #include <sys/file.h>
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <fcntl.h>
  24. #include <errno.h>
  25.  
  26. // An fstream can be in at most one of put mode, get mode, or putback mode.
  27. // Putback mode is a variant of get mode.
  28.  
  29. // In a filebuf, there is only one current position, instead of two
  30. // separate get and put pointers.  In get mode, the current posistion
  31. // is that of gptr(); in put mode that of pptr().
  32.  
  33. // The position in the buffer that corresponds to the position
  34. // in external file system is file_ptr().
  35. // This is normally egptr(), except in putback mode, when it is _save_egptr.
  36. // If the field _fb._offset is >= 0, it gives the offset in
  37. // the file as a whole of the start of the buffer (base()).
  38.  
  39. // PUT MODE:
  40. // If a filebuf is in put mode, pbase() is non-NULL and equal to base().
  41. // Also, epptr() == ebuf().
  42. // Also, eback() == gptr() && gptr() == egptr().
  43. // The un-flushed character are those between pbase() and pptr().
  44. // GET MODE:
  45. // If a filebuf is in get or putback mode, eback() != egptr().
  46. // In get mode, the unread characters are between gptr() and egptr().
  47. // The OS file position corresponds to that of egptr().
  48. // PUTBACK MODE:
  49. // Putback mode is used to remember "excess" characters that have
  50. // been sputbackc'd in a separate putback buffer.
  51. // In putback mode, the get buffer points to the special putback buffer.
  52. // The unread characters are the characters between gptr() and egptr()
  53. // in the putback buffer, as well as the area between save_gptr()
  54. // and save_egptr(), which point into the original reserve buffer.
  55. // (The pointers save_gptr() and save_egptr() are the values
  56. // of gptr() and egptr() at the time putback mode was entered.)
  57. // The OS position corresponds to that of save_egptr().
  58. //
  59. // LINE BUFFERED OUTPUT:
  60. // During line buffered output, pbase()==base() && epptr()==base().
  61. // However, ptr() may be anywhere between base() and ebuf().
  62. // This forces a call to filebuf::overflow(int C) on every put.
  63. // If there is more space in the buffer, and C is not a '\n',
  64. // then C is inserted, and pptr() incremented.
  65. //
  66. // UNBUFFERED STREAMS:
  67. // If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
  68.  
  69. void filebuf::init()
  70. {
  71.     _fb._fake = 0;
  72.     _fb._offset = 0;
  73.     _fb._fake = 0;
  74.     xsetflags(_S_IS_FILEBUF);
  75.     _fb._save_gptr = NULL;
  76.  
  77.     _link_in();
  78.     _fb._fileno = -1;
  79. }
  80.  
  81. void streambuf::_un_link()
  82. {
  83.     if (_flags & _S_LINKED) {
  84.     streambuf **f;
  85.     for (f = &_list_all; *f != NULL; f = &(*f)->xchain()) {
  86.         if (*f == this) {
  87.         *f = xchain();
  88.         break;
  89.         }
  90.     }
  91.     _flags &= _S_LINKED;
  92.     }
  93. }
  94.  
  95. void streambuf::_link_in()
  96. {
  97.     if (_flags & _S_LINKED) // Already linked.
  98.     abort();
  99.     _flags |= _S_LINKED;
  100.     xchain() = _list_all;
  101.     _list_all = this;
  102. }
  103.  
  104.  
  105. filebuf::filebuf()
  106. {
  107.     init();
  108. }
  109.  
  110. filebuf::filebuf(int fd)
  111. {
  112.     init();
  113.     attach(fd);
  114. }
  115.  
  116. filebuf::filebuf(int fd, char* p, int len)
  117. {
  118.     init();
  119.     attach(fd);
  120.     setbuf(p, len);
  121. }
  122.  
  123. filebuf::~filebuf()
  124. {
  125.     if (!(xflags() & _S_DELETE_DONT_CLOSE))
  126.     close();
  127.  
  128.     _un_link();
  129. }
  130.  
  131. filebuf* filebuf::open(const char *filename, int mode, int prot)
  132. {
  133.     if (is_open())
  134.     return NULL;
  135.     int posix_mode;
  136.     int read_write;
  137.     if ((mode & (ios::in|ios::out)) == (ios::in|ios::out))
  138.     posix_mode = O_RDWR, read_write = _S_CAN_READ+_S_CAN_WRITE;
  139.     else if (mode & (ios::out|ios::app))
  140.     posix_mode = O_WRONLY, read_write = _S_CAN_WRITE;
  141.     else if (mode & (int)ios::in)
  142.     posix_mode = O_RDONLY, read_write = _S_CAN_READ;
  143.     else
  144.     posix_mode = 0, read_write = 0;
  145.     if ((mode & (int)ios::trunc) || mode == (int)ios::out)
  146.     posix_mode |= O_TRUNC;
  147.     if (mode & ios::app)
  148.     posix_mode |= O_APPEND;
  149.     if (!(mode & (int)ios::nocreate) && mode != ios::in)
  150.     posix_mode |= O_CREAT;
  151.     if (mode & (int)ios::noreplace)
  152.     posix_mode |= O_EXCL;
  153.     int fd = ::open(filename, posix_mode, prot);
  154.     if (fd < 0)
  155.     return NULL;
  156.     _fb._fileno = fd;
  157.     xsetflags(read_write);
  158.     if (mode & ios::ate) {
  159.     if (seekoff(0, ios::end) == EOF)
  160.         return NULL;
  161.     }
  162.     return this;
  163. }
  164.  
  165. filebuf* filebuf::open(const char *filename, const char *mode)
  166. {
  167.     if (is_open())
  168.     return NULL;
  169.     int oflags = 0, omode;
  170.     int read_write;
  171.     int oprot = 0666;
  172.     switch (*mode++) {
  173.       case 'r':
  174.     omode = O_RDONLY;
  175.     read_write = _S_CAN_READ;
  176.     break;
  177.       case 'w':
  178.     omode = O_WRONLY;
  179.     oflags = O_CREAT|O_TRUNC;
  180.     read_write = _S_CAN_WRITE;
  181.     break;
  182.       case 'a':
  183.     omode = O_WRONLY;
  184.     oflags = O_CREAT|O_APPEND;
  185.     read_write = _S_CAN_WRITE;
  186.     break;
  187.       default:
  188.     errno = EINVAL;
  189.     return NULL;
  190.     }
  191.     if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
  192.     omode = O_RDWR;
  193.     read_write = _S_CAN_READ+_S_CAN_WRITE;
  194.     }
  195.     int fdesc = ::open(filename, omode|oflags, oprot);
  196.     if (fdesc < 0)
  197.     return NULL;
  198.     _fb._fileno = fdesc;
  199.     xsetflags(read_write);
  200.     return this;
  201. }
  202.  
  203. filebuf* filebuf::attach(int fd)
  204. {
  205.     if (is_open())
  206.     return NULL;
  207.     _fb._fileno = fd;
  208.     xsetflags(_S_CAN_READ|_S_CAN_WRITE|_S_DELETE_DONT_CLOSE);
  209.     return this;
  210. }
  211.  
  212. int filebuf::overflow(int c)
  213. {
  214.     if (pptr() == pbase() && c == EOF)
  215.     return 0;
  216.     if ((xflags() & _S_CAN_WRITE) == 0) // SET ERROR
  217.     return EOF;
  218.     if (is_reading()) {
  219.     if (pptr() != gptr() && pptr() > pbase())
  220.         if (do_flush())
  221.         return EOF;
  222.     setp(gptr(), ebuf());
  223.     setg(egptr(), egptr(), egptr());
  224.     }
  225.     if (allocate() > 0) {
  226.     if (xflags() & _S_LINE_BUF) setp(base(), base());
  227.     else setp(base(), ebuf());
  228.     setg(pbase(), pbase(), pbase());
  229.     }
  230.     int flush_only;
  231.     if (c == EOF) flush_only = 1, c = 0;
  232.     else flush_only = 0;
  233.     if (epptr() == pbase()) { // Line buffering
  234.     if (pptr() < ebuf() && !flush_only) {
  235.         xput_char(c);
  236.         if (c != '\n')
  237.         return (unsigned char)c;
  238.         else
  239.         flush_only = 1;
  240.     }
  241.     }
  242.     size_t to_do = out_waiting();
  243.     if (to_do > 0) {
  244.     char *ptr = pbase();
  245.     for (;;) {
  246.         size_t count = sys_write(ptr, to_do);
  247.         if (count == EOF)
  248.         return EOF;
  249.         _fb._offset += count;
  250.         to_do -= count;
  251.         if (to_do == 0)
  252.         break;
  253.         ptr += count;
  254.     }
  255.     if (xflags() & _S_LINE_BUF) setp(pbase(), pbase());
  256.     else setp(pbase(), epptr());
  257.     setg(egptr(), egptr(), egptr());
  258.     }
  259.     if (flush_only)
  260.     return c;
  261.     else
  262.     return sputc(c);
  263. }
  264.  
  265. int filebuf::underflow()
  266. {
  267. #if 0
  268.     /* SysV does not make this test; take it out for compatibility */
  269.     if (fp->_flags & __SEOF)
  270.     return (EOF);
  271. #endif
  272.  
  273.     if ((xflags() & _S_CAN_READ) == 0) // SET ERROR
  274.     return EOF;
  275.   retry:
  276.     if (gptr() < egptr())
  277.     return *(unsigned char*)gptr();
  278.     if (_fb._save_gptr) { // Free old putback buffer.
  279.     if (eback() != _fb._shortbuf)
  280.         FREE_BUF(eback());
  281.     _fb._save_gptr = NULL;
  282.     setg(base(), _fb._save_gptr, _fb._save_egptr); // Restore get area.
  283.     goto retry;
  284.     }
  285.  
  286.     allocate();
  287. #if 0
  288.     /* if not already reading, have to be reading and writing */
  289.     if ((fp->_flags & __SRD) == 0) {
  290.     if ((fp->_flags & __SRW) == 0)
  291.         return (EOF);
  292.     /* switch to reading */
  293.     if (fp->_flags & __SWR) {
  294.         if (fflush(fp))
  295.         return (EOF);
  296.         fp->_flags &= ~__SWR;
  297.         fp->_w = 0;
  298.         fp->_lbfsize = 0;
  299.     }
  300.     fp->_flags |= __SRD;
  301.     } else {
  302.     // We were reading.  If there is an ungetc buffer,
  303.     // we must have been reading from that.  Drop it,
  304.     // restoring the previous buffer (if any).  If there
  305.     // is anything in that buffer, return.
  306.     if (HASUB(fp)) {
  307.         FREEUB(fp);
  308.         if ((fp->_r = fp->_ur) != 0) {
  309.         fp->_p = fp->_up;
  310.         return (0);
  311.         }
  312.     }
  313.     }
  314. #endif
  315.     if ((xflags() & _S_LINE_BUF) || unbuffered()) {
  316.     // Flush all line buffered files before reading.
  317.     streambuf::flush_all_linebuffered();
  318.     }
  319.     if (pptr() > pbase())
  320.     if (do_flush()) return EOF;
  321.     int count = sys_read(base(), ebuf() - base());
  322.     if (count <= 0) {
  323.     if (count == 0)
  324.         xsetflags(_S_EOF_SEEN);
  325.     else
  326.         xsetflags(_S_ERR_SEEN), count = 0;
  327.     return EOF;
  328.     }
  329.     _fb._offset += count;
  330.     setg(base(), base(), base() + count);
  331.     return *(unsigned char*)gptr();
  332. }
  333.  
  334. int filebuf::pbackfail(int c)
  335. {
  336.     if (pbase() != NULL) { // is writing()
  337.     overflow(EOF);
  338.     // FIXME: check for writing!
  339.     }
  340.     if (_fb._save_gptr == NULL) { // No putback buffer.
  341.     _fb._save_gptr = gptr(); _fb._save_egptr = egptr();
  342.     setg(_fb._shortbuf, _fb._shortbuf+1, _fb._shortbuf+1);
  343.     }
  344.     else { // Increase size of existing putback buffer.
  345.     size_t new_size;
  346.     size_t old_size = egptr() - eback();
  347.     new_size = eback() == _fb._shortbuf ? 128 : 2 * old_size;
  348.     char* new_buf = ALLOC_BUF(new_size);
  349.     memcpy(new_buf+(new_size-old_size), eback(), old_size);
  350.     if (eback() != _fb._shortbuf)
  351.         FREE_BUF(eback());
  352.     setg(new_buf, new_buf+(new_size-old_size), new_buf+new_size);
  353.     }
  354.     gbump(-1);
  355.     *gptr() = c;
  356.     return (unsigned char)c;
  357. }
  358.  
  359. int filebuf::do_flush()
  360. {
  361.     if (egptr() != pbase()) {
  362.     long new_pos = sys_seek(pbase()-egptr(), ios::cur);
  363.     if (new_pos == -1)
  364.         return EOF;
  365.     }
  366.     long to_do = pptr()-pbase();
  367.     char* ptr = pbase();
  368.     while (to_do > 0) {
  369.     size_t count = sys_write(ptr, to_do);
  370.     if (count == EOF)
  371.         return EOF;
  372.     if (_fb._offset >= 0)
  373.         _fb._offset += count;
  374.     to_do -= count;
  375.     ptr += count;
  376.     }
  377.     setg(base(), pptr(), pptr());
  378.     setp(base(), base());
  379.     return 0;
  380. }
  381.  
  382. int filebuf::sync()
  383. {
  384. //    char* ptr = cur_ptr();
  385.     if (pptr() > pbase())
  386.     do_flush();
  387.     if (gptr() != egptr()) {
  388.     if (sys_seek(gptr() - egptr(), ios::cur) == EOF)
  389.         return EOF;
  390.     }
  391.     // FIXME: Handle putback mode!
  392. //    setg(base(), ptr, ptr);
  393.     return 0;
  394. }
  395.  
  396. streampos filebuf::seekoff(streamoff offset, _seek_dir dir, int mode)
  397. {
  398.     fpos_t result, new_offset, delta;
  399.     int count;
  400.  
  401.     // Flush unwritten characters.
  402.     // (This may do an unneeded write if we seek within the buffer.
  403.     // But to be able to switch to reading, we would need to set
  404.     // egptr to ptr.  That can't be done in the current design,
  405.     // which assumes file_ptr() is egptr.  Anyway, since we probably
  406.     // end up flushing when we close(), it doesn't make much difference.)
  407.     if (pptr() > pbase())
  408.     do_flush();
  409.  
  410.     if (unbuffered() || base() == NULL)
  411.     goto dumb;
  412.     // FIXME: What if buffer already allocated even though unbuffered()
  413.     switch (dir) {
  414.       case ios::cur:
  415.     if (_fb._offset < 0) {
  416.         _fb._offset = sys_seek(0, ios::cur);
  417.         if (_fb._offset < 0)
  418.         return EOF;
  419.     }
  420.     // Make offset absolute, assuming current pointer is file_ptr().
  421.     offset += _fb._offset;
  422.     // Now adjust for unread/unflushed characters:
  423.     // FIXME - this stuff needs more thought.
  424. #if 0
  425.     offset += cur_ptr() - file_ptr();
  426.     // FIXME - might be confused if there is a putback buffer.
  427. #else
  428.     offset -= egptr() - gptr(); // Subtract unread characters, if any.
  429.     if (_fb._save_gptr) // Putback mode
  430.         offset -= _fb._save_egptr - _fb._save_gptr;
  431.     // Normally, egptr()==pbase(), but if we earlier switched from
  432.     // get to put mode, the following will (correctly) decrease offset.
  433.     // However, it won't work if there is a putback buffer - FIXME!
  434.     offset += pptr() - egptr(); // Add unflushed characters, in put mode.
  435. #endif
  436.     dir = ios::beg;
  437.     break;
  438.       case ios::beg:
  439.     break;
  440.       case ios::end:
  441.     struct stat st;
  442.     if (sys_stat(&st) == 0 && (st.st_mode & S_IFMT) == S_IFREG) {
  443.         offset += st.st_size;
  444.         dir = ios::beg;
  445.     }
  446.     else
  447.         goto dumb;
  448.     }
  449.     // At this point, dir==ios::beg.
  450.     // FIXME: Handle case of there being a putback buffer!
  451.     // If destination is within current buffer, optimize:
  452.     if (_fb._offset >= 0) {
  453.     fpos_t rel_offset = offset - _fb._offset
  454.         + (file_ptr()-base()); // Offset relative to base().
  455.     if (rel_offset >= 0 && rel_offset <= egptr() - base()) {
  456.         setg(base(), base() + rel_offset, egptr());
  457.         setp(base(), base());
  458.         return offset;
  459.         }
  460.     }
  461.  
  462.     // Try to seek to a block boundary, to improve kernal page management.
  463.     new_offset = offset & ~(ebuf() - base() - 1);
  464.     delta = offset - new_offset;
  465.     if (delta > ebuf() - base()) {
  466.     new_offset = offset;
  467.     delta = 0;
  468.     }
  469.     result = sys_seek(new_offset, ios::beg);
  470.     if (result < 0)
  471.     return EOF;
  472.     _fb._offset = result;
  473.     count = sys_read(base(), ebuf()-base());
  474.     if (count < 0)
  475.     return EOF;
  476.     setg(base(), base(), base()+count);
  477.     xflags(xflags() & ~ _S_EOF_SEEN);
  478.     return result;
  479.   dumb:
  480.     if (_fb._save_gptr != NULL) { // Get rid of putback buffer.
  481.     if (eback() != _fb._shortbuf)
  482.         FREE_BUF(eback());
  483.     _fb._save_gptr = NULL;
  484.     }
  485.     result = sys_seek(offset, dir);
  486.     if (result != EOF) {
  487.     _flags &= ~_S_EOF_SEEN;
  488.     }
  489.     char* start = unbuffered() ? _fb._shortbuf : base();
  490.     setg(start, start, start);
  491.     setp(start, start);
  492.     return result;
  493. }
  494.  
  495. filebuf* filebuf::close()
  496. {
  497.     if (!is_open())
  498.     return NULL;
  499.  
  500.     // Flush.
  501.     overflow(EOF);
  502.  
  503.     /* Free the buffer's storage.  */
  504.     // Or should that be done only on destruction???
  505.     if (_base != NULL && !(_flags & _S_USER_BUF)) {
  506.     FREE_BUF(_base);
  507.     _base = NULL;
  508.     }
  509.  
  510.     int status = sys_close();
  511.  
  512.     _un_link();
  513.     _flags = _IO_MAGIC | _S_IS_FILEBUF;
  514.     _fb._fileno = EOF;
  515.  
  516.     return status < 0 ? NULL : this;
  517. }
  518.  
  519. int filebuf::sys_read(char* buf, size_t size)
  520. {
  521.     return ::read(_fb._fileno, buf, size);
  522. }
  523.  
  524. fpos_t filebuf::sys_seek(fpos_t offset, _seek_dir dir)
  525. {
  526.     return ::lseek(fd(), offset, (int)dir);
  527. }
  528.  
  529. long filebuf::sys_write(const void *buf, long n)
  530. {
  531.     return ::write(fd(), buf, n);
  532. }
  533.  
  534. int filebuf::sys_stat(void* st)
  535. {
  536.     return ::_fstat(fd(), (struct stat*)st);
  537. }
  538.  
  539. int filebuf::sys_close()
  540. {
  541.     return ::close(fd());
  542. }
  543.  
  544. int filebuf::sputn(const char *s, int n)
  545. {
  546.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  547.     return streambuf::sputn(s, n);
  548. }
  549.  
  550. int filebuf::sgetn(char *s, int n)
  551. {
  552.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  553.     return streambuf::sgetn(s, n);
  554. }
  555.